package com.atguigu.gmall.cart.service.Impl;

import com.atguigu.gmall.cart.mapper.CartInfoMapper;
import com.atguigu.gmall.cart.service.CartAsyncService;
import com.atguigu.gmall.cart.service.CartService;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.common.util.DateUtil;
import com.atguigu.gmall.model.cart.CartInfo;
import com.atguigu.gmall.model.product.SkuInfo;
import com.atguigu.gmall.product.client.ProductFeignClient;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;


import java.sql.Timestamp;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class CartServiceImpl implements CartService {
    @Autowired
    private CartInfoMapper cartInfoMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private ProductFeignClient productFeignClient;


    /**
     *优化：
     *  现在操作都是同步操作
     *      异步数据库（另一个线程执行）同步缓存（当前线程）
     */
    //异步注入
    @Autowired
    private CartAsyncService cartAsyncService;
    @Override
    public void addToCart(Long skuId, String userId, Integer skuNum) {

        //1 添加商品之前，查看购物车中是否有该商品
        // true：表示有，商品数量添加 false：没有，直接添加购物车
        //2 将数据同步到redis
        //select * from cart_info where sku_Id = ？and userId = ？;
        //数据类型hash+key   hash（key,field,volue）
        //key = user + userId : cart, 谁的购物车 field=skuId volue=cartInfo
        String cartKey = this.getcartKey(userId);
        //判断缓存中是否有数据
        if (!redisTemplate.hasKey(cartKey)){
            //加载数据库放入缓存
            this.loadCartCache(userId);
        }
        //修改表
//        QueryWrapper<CartInfo> wrapper = new QueryWrapper<>();
//        wrapper.eq("user_id",userId);
//        wrapper.eq("sku_id",skuId);
//        CartInfo cartInfoExist  = cartInfoMapper.selectOne(wrapper);
        //查缓存
        CartInfo cartInfoExist = (CartInfo) redisTemplate.boundHashOps(cartKey).get(skuId.toString());


        //不为空，当前购物车中有该商品
        if (cartInfoExist!=null){
            cartInfoExist.setSkuNum(cartInfoExist.getSkuNum()+skuNum);
            //初始化实时价格 将数据库中的cartPrice 赋值给skuPrice
            cartInfoExist.setSkuPrice(productFeignClient.getSkuPrice(skuId));
            //修改更新时间
            cartInfoExist.setUpdateTime(new Timestamp(new Date().getTime()));
            //修改数据库执行语句
//            cartInfoMapper.updateById(cartInfoExist);
            cartAsyncService.updateCartInfo(cartInfoExist);
            //修改缓存
            //存储数据
//            redisTemplate.opsForHash().put(cartKey,skuId.toString(),cartInfoExist);

        }else {
            //第一次添加购物车
            CartInfo cartInfo = new CartInfo();
            SkuInfo skuInfo = productFeignClient.getSkuInfo(skuId);
            cartInfo.setUserId(userId);
            cartInfo.setSkuId(skuId);
            //在初始化得时候，添加购物车得价格=skuInfo.price
            cartInfo.setCartPrice(skuInfo.getPrice());
            //数据库不存在得，购物车价格=skuInfo.price
            cartInfo.setSkuPrice(skuInfo.getPrice());
            cartInfo.setSkuNum(skuNum);
            cartInfo.setImgUrl(skuInfo.getSkuDefaultImg());
            cartInfo.setSkuName(skuInfo.getSkuName());
            cartInfo.setCreateTime(new Timestamp(new Date().getTime()));
            cartInfo.setUpdateTime(new Timestamp(new Date().getTime()));
            //执行数据库操作
//            cartInfoMapper.insert(cartInfo);
            cartAsyncService.saveCartInfo(cartInfo);
            //放入库
            //存储数据
//            redisTemplate.opsForHash().put(cartKey,skuId.toString(),cartInfo);
            cartInfoExist = cartInfo;
        }

        //存储数据hash（key,field,volue）
//            redisTemplate.opsForHash().put(cartKey,skuId.toString(),cartInfoExist);
        redisTemplate.boundHashOps(cartKey).put(skuId.toString(),cartInfoExist);
        //设置过期时间
        this.setCartKeyExpire(cartKey);


    }

    @Override
    public List<CartInfo> getCartList(String userId, String userTempId) {
        List<CartInfo> cartInfolist = new ArrayList<>();



        //查询业务逻辑，先查询缓存，缓存没有查询数据库
        //判断 //如果用户Id为空则按照临时用户Id查询
        //走这里表示userId为空。那么你一定能保证临时用户userTempId就不为空嘛！
        if (StringUtils.isEmpty(userId)){

           cartInfolist = getCartList(userTempId);
        }
        //判断用户ID不为空，则按照用户ID来查询
        if (!StringUtils.isEmpty(userId)){
            //可能发生合并，查看未登录购物车的数据
            if (StringUtils.isEmpty(userTempId)){
                //直接返回 userTempId = null
                cartInfolist = getCartList(userId);
            }else {
                List<CartInfo> cartInfoNologlist = getCartList(userTempId);
                if (!CollectionUtils.isEmpty(cartInfoNologlist)){
                    //合并

                    //cartInfoNologlist未登录用户购物车数据，登录用户购物车数据使用userId来查
                    cartInfolist = this.mergeToCartList(cartInfoNologlist,userId);
                    //删除未登录购物车数据
                    this.deleteCartList(userTempId);
                }else {
                    //直接返回 cartInfoNologlist = null
                    cartInfolist = getCartList(userId);
                }
            }
        }
        return cartInfolist;
    }

    @Override
    public void checkCart(String userId, Integer isChecked, Long skuId) {
        //更新数据库
        cartAsyncService.checkCart(userId,isChecked,skuId);
        //先获取数据
        //更新缓存
        String  cartKey = this.getcartKey(userId);
        //从缓存中获取cartInfo
        CartInfo cartInfo = (CartInfo) redisTemplate.boundHashOps(cartKey).get(skuId.toString());
        //修改数据
        cartInfo.setIsChecked(isChecked);
        //放入这个缓存
        redisTemplate.boundHashOps(cartKey).put(skuId.toString(),cartInfo);
        //重新设置一个过期时间
//     灵活应用   this.setCartKeyExpire(cartKey);

    }

    @Override
    public List<CartInfo> getCartCheckedList(String userId) {
        List<CartInfo>cartInfoList = new ArrayList<>();
        // 定义key user:userId:cart
        String cartKey = this.getcartKey(userId);
        List<CartInfo> cartCachInfoList  = redisTemplate.opsForHash().values(cartKey);
        if (null!=cartCachInfoList && cartCachInfoList.size()>0){
            for (CartInfo cartInfo : cartCachInfoList) {
                // 获取选中的商品！
                if (cartInfo.getIsChecked().intValue()==1){
                    cartInfoList.add(cartInfo);
                }
            }
        }
        return cartInfoList;
    }

    @Override
    public void deleteCart(Long skuId, String userId) {
        String cartKey  = getcartKey(userId);
        cartAsyncService.deleteCartInfo(userId,skuId);
        //获取缓存对象
        BoundHashOperations<String, String, CartInfo> hashOperations  = redisTemplate.boundHashOps(cartKey);
        if (hashOperations.hasKey(skuId.toString())){
            hashOperations.delete(skuId.toString());
        }
    }

    //删除购物车
    private void deleteCartList(String userTempId) {
        //删除数据库，删除缓存
        CartInfo cartInfo = new CartInfo();
        cartInfo.setUserId(userTempId);
        cartAsyncService.deleteCartInfo(cartInfo);
        //获取缓存key
        String cartKey = this.getcartKey(userTempId);
        if (redisTemplate.hasKey(cartKey)){
            redisTemplate.delete(cartKey);
        }

    }

    //合并购物车方法
    //cartInfoNologlist 未登录的购物车数据   userId用户ID
    private List<CartInfo> mergeToCartList(List<CartInfo> cartInfoNologlist, String userId) {
         /*
                    demo1:
                        登录：
                            37 1
                            38 1
                        未登录：
                            37 1
                            38 1
                            39 1
                        合并之后的数据
                            37 2
                            38 2
                            39 1
                     demo2:
                         未登录：
                            37 1
                            38 1
                            39 1
                            40  1
                          合并之后的数据
                            37 1
                            38 1
                            39 1
                            40  1
                     */

         //获取用户已登录数据
        List<CartInfo> cartInfoLoginList = this.getCartList(userId);
        //判断用户购物车数据是否为空（需要将未登录的数据添加到数据库）
//        if (CollectionUtils.isEmpty(cartInfoLoginList))return cartInfoNologlist;
        //当登录购物车数据不为空  合并条件skuId
        if(!CollectionUtils.isEmpty(cartInfoLoginList)){
            //CartInfo::getSkuId,cartInfo
            //map的key = skuId value = cartInfo
            Map<Long, CartInfo> longCartInfoMap = cartInfoLoginList.stream().collect(Collectors.toMap(CartInfo::getSkuId, cartInfo -> cartInfo));
            //判断SkuId在已登录的Map中是否有Key
            //遍历集合
            for (CartInfo cartInfo : cartInfoNologlist) {
                Long skuId = cartInfo.getSkuId();
                //skuId是否存在  处理两个购物车相同的数据
                if (longCartInfoMap.containsKey(skuId)){
                    //相等表示，未登录购物车的商品在已登录购物车中已存在
                    //数量相加   用skuID 对应获取登录的cartInfo对象
                    CartInfo cartInfoLogin = longCartInfoMap.get(skuId);
                    //赋值商品数量
                    cartInfoLogin.setSkuNum(cartInfoLogin.getSkuNum() + cartInfo.getSkuNum());
                    //更新一个cartInfo的更新时间
                    cartInfoLogin.setUpdateTime(new Timestamp(new Date().getTime()));
                    //合并购物车时的选择状态
                    //以未登录选中状态为基准
                    if (cartInfo.getIsChecked().intValue()==1){
                        cartInfoLogin.setIsChecked(1);
                    }



                    //更新数据库
                    //同步更新
                    QueryWrapper<CartInfo> wrapper = new QueryWrapper<>();
                    wrapper.eq("user_id",cartInfoLogin.getImgUrl());
                    wrapper.eq("sku_id",cartInfoLogin.getSkuId());
                    //        cartInfoMapper.updateById(cartInfo);
                    //        cartInfo第一个参数相当于，第二个参数相当于更新条件
                    cartInfoMapper.update(cartInfoLogin,wrapper);
//                    cartInfoMapper.updateById(cartInfoLogin);
//                    异步更新在这不能使用：因为会出现数据不一致，如果要使用异步进行优化，
//                    建议使用在添加购物车时使用异步操作，因为添加后不需要立即查询数据
                    cartAsyncService.updateCartInfo(cartInfoLogin);

                }else {
                    //处理已登录购物车没有的未登录购物车中的数据
                    //赋值登录的用户ID
                    cartInfo.setUserId(userId);
                    //更新新增时间
                    cartInfo.setCreateTime(new Timestamp(new Date().getTime()));
                    //更新修改时间
                    cartInfo.setUpdateTime(new Timestamp(new Date().getTime()));
                    cartInfoMapper.insert(cartInfo);
                }
            }

        }
        //从数据库中获取到最新合并的数据，然后放入缓存
        List<CartInfo> cartInfoList = this.loadCartCache(userId);
        return cartInfoList;
    }

    //根据用户Id查询购物车列表
    private List<CartInfo> getCartList(String userId) {
        //查询业务逻辑，先查询缓存，缓存没有查询数据库
        String cartKey = this.getcartKey(userId);

        List<CartInfo> cartInfosList = new ArrayList<>();
        //这个用户ID可以代表登录，也可以代表未登录
        //判断临时用户ID是否为空
        if(StringUtils.isEmpty(userId)){
            return cartInfosList;
        }
//        if (redisTemplate.hasKey(cartKey)){
//        }
        //获取缓存中的数据，获取购物车的value的数据  hvals(cartKey)
        cartInfosList = redisTemplate.boundHashOps(cartKey).values();
//        cartInfosList = redisTemplate.opsForHash().values(cartkey);
        //判断
        if (!CollectionUtils.isEmpty(cartInfosList)){
            //Comparator 外部--自定义比较器
//            Comparable 内部比较器
            //查询的时候排序：根据修改时间
            cartInfosList.sort(new Comparator<CartInfo>() {
                @Override
                public int compare(CartInfo o1, CartInfo o2) {
                    return DateUtil.truncatedCompareTo(o2.getUpdateTime(),o1.getUpdateTime(), Calendar.SECOND);
                }
            });

            return cartInfosList;
        }else {
            //缓存中没有数据，从数据库中获得，并存的缓存中
            cartInfosList = loadCartCache(userId);
            return cartInfosList;
        }
    }

    public List<CartInfo> loadCartCache(String userId) {
        //从数据库中获得，并存的缓存中
        //select * from cart_info where user_id = ?
        List<CartInfo> cartInfosList = cartInfoMapper.selectList(new QueryWrapper<CartInfo>().eq("user_id", userId));
        //判断
        if (CollectionUtils.isEmpty(cartInfosList)){
            return cartInfosList;
        }
        //数据库中的数据不为空
        //放到缓存要获取到Key
        String cartKey = this.getcartKey(userId);
        //放入缓存  hash(key,field,value); hmset(key,map)
        HashMap<String, Object> map = new HashMap<>();
        for (CartInfo cartInfo : cartInfosList) {
            //因为在缓存中没有数据，查询了一次数据库，有可能价格发生变化
            cartInfo.setSkuPrice(productFeignClient.getSkuPrice(cartInfo.getSkuId()));


            map.put(cartInfo.getSkuId().toString(),cartInfo);
//            redisTemplate.boundHashOps(cartKey).put(cartInfo.getSkuId().toString(),cartInfo);
        }
        //存储完成
        redisTemplate.boundHashOps(cartKey).putAll(map );
        //设置一个过期时间
        this.setCartKeyExpire(cartKey);
        //返回数据
        return cartInfosList;
    }

    public Boolean setCartKeyExpire(String cartKey) {
        return redisTemplate.expire(cartKey, RedisConst.USER_CART_EXPIRE, TimeUnit.SECONDS);
    }

    public String getcartKey(String userId) {
       return RedisConst.USER_KEY_PREFIX + userId + RedisConst.USER_CART_KEY_SUFFIX;
    }
}
